package io.coderyeah.ymcc.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import io.coderyeah.ymcc.constant.RedisConstants;
import io.coderyeah.ymcc.domain.KillActivity;
import io.coderyeah.ymcc.domain.KillCourse;
import io.coderyeah.ymcc.dto.KillParamDTO;
import io.coderyeah.ymcc.dto.PreOrderDTO;
import io.coderyeah.ymcc.mapper.KillCourseMapper;
import io.coderyeah.ymcc.service.IKillActivityService;
import io.coderyeah.ymcc.service.IKillCourseService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import io.coderyeah.ymcc.util.AssertUtil;
import io.coderyeah.ymcc.util.CodeGenerateUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author coderyeah
 * @since 2022-11-08
 */
@Slf4j
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {
    @Autowired
    private IKillActivityService activityService;
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void save(KillCourse killCourse) {
        // 根据活动id查询活动
        KillActivity killActivity = activityService.selectById(killCourse.getActivityId());
        AssertUtil.isNotNull(killActivity, "该活动不存在！！！");
        // 查询该课程是否已经发布
        Wrapper<KillCourse> wrapper = new EntityWrapper<>();
        wrapper.eq("course_id", killActivity.getId());
        wrapper.eq("activity_id", killActivity.getId());
        KillCourse selectOne = selectOne(wrapper);
        AssertUtil.isNull(selectOne, "该课程已经发布");

        // 设置起始时间
        killCourse.setStartTime(killActivity.getStartTime());
        killCourse.setEndTime(killActivity.getEndTime());
        killCourse.setTimeStr(killActivity.getTimeStr());
        killCourse.setKillLimit(1);// 每一个用户限购数量默认为1
        killCourse.setCreateTime(new Date());

        // 判断该活动是否发布
        if (killActivity.getPublishStatus().equals(KillActivity.KILL_OK_PUBLISH)) {
            // 活动已经发布
            killCourse.setPublishStatus(KillCourse.KILLING);
            // 保存秒杀
            insert(killCourse);
            // 将该课程存入redis
            redisTemplate.opsForHash().put(RedisConstants.KILL_ACTIVITY + killActivity.getId().toString(), killCourse.getId().toString(), killCourse);
        } else {
            killCourse.setPublishStatus(KillCourse.KILL_WAIT_PUBLISH);
            // 保存秒杀
            insert(killCourse);
        }
    }

    /*
     查询redis秒杀课程
     */
    @Override
    public List<KillCourse> onlineAll() {
        // 获取所有活动key
        Set<Object> keys = redisTemplate.keys("activity:*");
        AssertUtil.isNotNull(keys, "暂无活动发布哦");
        List<KillCourse> list = new ArrayList<>();
        for (Object key : keys) {
            // 获取所有字段
            Set<Object> fields = redisTemplate.opsForHash().keys(key);
            log.info("fields:{}", fields);
            fields.forEach(field -> {
                KillCourse killCourse = (KillCourse) redisTemplate.opsForHash().get(key, field);
                list.add(killCourse);
            });
        }
        return list;
    }

    @Override
    public KillCourse onlineOne(String killId, String activeId) {
        // 通过key和字段查找秒杀课程
        Object o = redisTemplate.opsForHash().get(RedisConstants.KILL_ACTIVITY + activeId, killId);
        AssertUtil.isNotNull(o, "此课程已下架");
        return (KillCourse) o;
    }

    @Override
    public String kill(KillParamDTO dto) {
        // 获取当前登陆人 fixme
        Long loginId = 3L;
        // redis获取用户是否购买此课程  key 3:killCourseId value orderSn
        String key = loginId + ":" + dto.getKillCourseId();
        Object o = redisTemplate.opsForValue().get(key);
        AssertUtil.isNull(o, "您已购买过此课程");
        // 查看购买课程是否合法
        KillCourse killCourse = onlineOne(dto.getKillCourseId().toString(), dto.getActivityId().toString());
        // 判断是否处于秒杀中
        AssertUtil.isTrue(killCourse.isKilling(), "活动未开始,请稍后再试");
        //库存的校验  消费信号量--如果消费不成功，卖完了
        RSemaphore semaphore = redissonClient.getSemaphore(killCourse.getId().toString());
        // 消费一个信号量
        boolean b = semaphore.tryAcquire(1);
        AssertUtil.isTrue(b, "手速太慢了,被抢光了");
        // 生成订单号
        String orderSn = CodeGenerateUtils.generateOrderSn(loginId);
        // 生成预定单 消息
        PreOrderDTO preOrderDTO = new PreOrderDTO(orderSn, killCourse.getCourseId(), dto.getKillCourseId(), loginId, killCourse.getKillPrice());
        // 预订单数据放到redis里面
        redisTemplate.opsForValue().set(orderSn, preOrderDTO);
        // 用户购买记录存入redis
        redisTemplate.opsForValue().set(key, orderSn);
        return orderSn;
    }
}
